package com.pingao.mp.modular.sys.service.impl;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pingao.mp.config.dict.DictConfig;
import com.pingao.mp.config.dict.DictItem;
import com.pingao.mp.modular.sys.entity.*;
import com.pingao.mp.modular.sys.mapper.*;
import com.pingao.mp.modular.sys.service.UserService;
import com.pingao.mp.utils.Constant;
import com.pingao.mp.utils.Md5;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author 闻品高
 * @since 2018-09-26
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private DictConfig dictConfig;

    private final static Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);

    public User getSessionUser() {
        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();
        Object loginUserNameObj = session.getAttribute(Constant.LoginUser);
        if (loginUserNameObj != null) {
            return (User) loginUserNameObj;
        }
        return getUserByUsername(currentUser.getPrincipal().toString());
    }

    @Override
    public Map<String, Object> myInfo(User user) {
        Map<String, Object> map = new HashMap<>();
        //判断修改的邮箱是否被其他用户占用
        List<User> emailUsers = baseMapper.selectListJoin(new QueryWrapper<User>().eq(User.COL_EMAIL_JOIN, user.getEmail()).notIn(User.COL_ID_JOIN, user.getId()).isNull(User.COL_DELETE_TIME_JOIN));
        if (emailUsers != null && emailUsers.size() > 0) {
            map.put("status", false);
            map.put("msg", user.getEmail() + "，邮箱已经被其他用户占用，请填写其他邮箱");
            return map;
        }

        if ("".equals(user.getPassword())) {
            user.setPassword(null);
        } else {
            user.setPassword(Md5.md5(user.getPassword()));
        }
        user.setUpdateTime(LocalDateTime.now());
        if (baseMapper.updateById(user) > 0) {
            map.put("status", true);
            map.put("msg", "修改成功");
        } else {
            map.put("status", false);
            map.put("msg", "修改失败");
        }
        return map;
    }

    @Override
    public String getToken(User user) {
        Date iatDate = new Date();
        Calendar nowTime = Calendar.getInstance();
        nowTime.add(Calendar.MINUTE, 100);//token有效时长100分钟
        Date expiresDate = nowTime.getTime();
        String token = "";
        token = JWT.create().withIssuedAt(iatDate).withExpiresAt(expiresDate).withAudience(user.getId().toString())
                .sign(Algorithm.HMAC256(user.getPassword()));
        return token;
    }

    @Override
    public User getUserByIdForRoles(Long userId) {
        return baseMapper.selectByIdJoin(userId);
    }

    /**
     * 根据用户名找用户对应的角色
     *
     * @param username 登录用户名
     * @return 对应的角色集合（该系统只是一个人用户对应一个角色，这里返回集合是因为方便扩展）
     */
    public Set<Role> getRolesByUsername(String username) {
        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();
        Object loginUserRolesObj = session.getAttribute(Constant.LoginUserRoles);
        if (loginUserRolesObj != null) {
            return (Set<Role>) loginUserRolesObj;
        }
        Set<Role> roles = new HashSet<>();
        User user = getUserByUsername(username);
        if (user != null) {
            List<Role> list = roleMapper.selectList(new QueryWrapper<Role>().eq(Role.COL_ID, user.getRoleId()).isNull(Role.COL_DELETE_TIME));
            roles.addAll(list);
            List<Role> userRoles = roleMapper.selectExtraUserRolesByUserId(user.getId());
            roles.addAll(userRoles);
        }

        session.setAttribute(Constant.LoginUserRoles, roles);
        return roles;
    }

    /**
     * 根据角色code获取该角色的权限集合
     *
     * @param roles 角色集合，原来是当个roleCode传递，这样的话，当第一个code执行了就会保存在session中，以至于以后再传递code也只会返回第一个code的权限。现在改成所有的角色一起传递进来
     * @return 权限集合（这里定义为相应菜单的模块名.控制器名.方法名，比如：sys.user.add）
     */
    public Set<String> getPermissionsByRole(Set<Role> roles) {
        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();
        Object loginUserPermissionsObj = session.getAttribute(Constant.LoginUserPermissions);
        if (loginUserPermissionsObj != null) {
            return (Set<String>) loginUserPermissionsObj;
        }
        Set<String> permissions = new HashSet<>();

        List<Long> roleIds = new ArrayList<>();
        roles.forEach(role -> roleIds.add(role.getId()));
        List<Permission> list = permissionMapper.selectList(new QueryWrapper<Permission>().in(Permission.COL_ROLE_ID, roleIds).isNull(Permission.COL_DELETE_TIME).select(Permission.COL_MENU_ID));
        if (list == null || list.size() < 1) {
            return permissions;
        }
        List<Long> menuIds = new ArrayList<>();
        list.forEach(permission -> menuIds.add(permission.getMenuId()));
        List<Menu> menus = menuMapper.selectList(new QueryWrapper<Menu>().in(Menu.COL_ID, menuIds).isNull(Menu.COL_DELETE_TIME));
        for (Menu menu : menus) {
            String module = menu.getModuleName();
            String action = menu.getActionName();
            String function = menu.getFunctionName();
            permissions.add(module + "." + action + "." + function);
        }
        session.setAttribute(Constant.LoginUserPermissions, permissions);
        return permissions;
    }

    public User getUserByUsername(String username) {
        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();
        Object loginUserNameObj = session.getAttribute(Constant.LoginUser);
        if (loginUserNameObj != null) {
            return (User) loginUserNameObj;
        }
        User user = baseMapper.selectOneJoin(new QueryWrapper<User>().eq(User.COL_NAME_JOIN, username).isNull(User.COL_DELETE_TIME_JOIN));
        if (user == null) {
            throw new UnknownAccountException("找不到该用户");
        }
        Role role = roleMapper.selectById(user.getRoleId());
        user.setRoleName(role != null ? role.getName() : "角色不存在");
        session.setAttribute(Constant.LoginUserName, username);
        session.setAttribute(Constant.LoginUser, user);

        return user;
    }

    @Override
    public Map<String, Object> getUserList(Integer pageNumber, Integer pageSize, String searchText, String sortName, String sortOrder, Integer roleId) {
        Map<String, Object> map = new HashMap<>();
        Page<User> page = new Page<>(pageNumber, pageSize);
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("1", "1");

        if (searchText != null && !"".equals(searchText)) {
            wrapper.and(q -> q.like(User.COL_NAME_JOIN, searchText).or().like(User.COL_NICK_JOIN, searchText).or().like(User.COL_EMAIL_JOIN, searchText).or().like(User.COL_ADDRESS_JOIN, searchText).or().like(User.COL_ROLE_NAME, searchText));
        }

        if (roleId != null) {
            wrapper.eq(User.COL_ROLE_ID_JOIN, roleId);
        }

        switch (sortName) {
            case "createTime":
                sortName = User.COL_CREATE_TIME_JOIN;
                break;
            case "updateTime":
                sortName = User.COL_UPDATE_TIME_JOIN;
                break;
            case "roleName":
                sortName = User.COL_ROLE_ID_JOIN;
                break;
        }

        if (sortOrder.equals("desc") || sortOrder.equals("DESC")) {
            wrapper.orderByDesc(sortName);
        } else {
            wrapper.orderByAsc(sortName);
        }

        baseMapper.selectPageJoin(page, wrapper);

        List<User> list = page.getRecords();
        addDictFields(list);
        map.put("rows", list);
        map.put("total", page.getTotal());
        return map;
    }

    @Override
    public Map<String, Object> login(String username, String password, Integer rememberMe) {
        Map<String, Object> map = new HashMap<>();
        map.put("username", username);
        map.put("password", password);
        UsernamePasswordToken token = new UsernamePasswordToken(username, Md5.md5(password));
        if (rememberMe != null && rememberMe.equals(1)) {
            token.setRememberMe(true);
        }
        Subject currentUser = SecurityUtils.getSubject();
        try {
            currentUser.login(token);
            //认证成功
            map.put("status", true);
            map.put("msg", "登录成功");
        } catch (UnknownAccountException uae) {
            LOGGER.info("There is no user with username of " + token.getPrincipal());
            //找不到用户
            map.put("status", false);
            map.put("msg", "没有该用户" + token.getPrincipal());
            map.put("exception", uae.getMessage());
        } catch (IncorrectCredentialsException ice) {
            LOGGER.info("Password for account " + token.getPrincipal() + " was incorrect!");
            map.put("status", false);
            map.put("msg", " 密码或用户名错误");
            map.put("exception", ice.getMessage());
        } catch (LockedAccountException lae) {
            LOGGER.info("The account for username " + token.getPrincipal() + " is locked.  " +
                    "Please contact your administrator to unlock it.");
            map.put("status", false);
            map.put("msg", " 用户被锁定");
            map.put("exception", lae.getMessage());

        } catch (AuthenticationException ae) {
            //unexpected condition?  error?
            map.put("status", false);
            map.put("msg", " 认证失败");
            map.put("exception", ae.getMessage());
        }
        return map;
    }

    private void addDictFields(List<User> list) {
        for (User user : list) {
            for (DictItem gender : dictConfig.getGenders()) {
                if (gender.getValue().equals(user.getGender())) {
                    user.setGenderName(gender.getName());
                    break;
                }
            }
        }
    }
}
